﻿

#region Using Statements

// System References
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading;

// XNA References
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

// Internal References
using Freeform.Framework;
using Freeform.Framework.Utility;
using Freeform.Framework.Geometry;

#endregion


namespace Freeform.Framework.Graphics
{
    /// <summary>
    /// The camera controls where the entities appear in the viewport.
    /// </summary>
    public class Camera : Element
    {

        #region Members

        /// <summary>
        /// Gets the viewport that this camera belongs to.
        /// </summary>
        public virtual Viewport2D Viewport { get; private set; }


        /// <summary>
        /// Gets the world that this camera is observing.
        /// </summary>
        public virtual World World { get; private set; }


        /// <summary>
        /// Gets the spatial properties of the camera.
        /// The position and size are in world coordinates. The position refers to the top left of the camera.
        /// The size governs how much fits in the viewport (the zoom effectively.)
        /// </summary>
        public virtual SpatialProperties SpatialProperties { get; private set; }

        #endregion


        #region Init

        /// <summary>
        /// Creates a new instance of a camera.
        /// </summary>
        /// <param name="viewport">The viewport that this camera belongs to.</param>
        internal Camera(Viewport2D Viewport)
        {
            this.Viewport = Viewport;
            World = Viewport.World;
            SpatialProperties = new SpatialProperties();
            IsEnabled = true;
        }

        #endregion


        #region Update

        /// <summary>
        /// Allows the camera to update itself.
        /// </summary>
        /// <param name="time">Provides a snapshot of timing values.</param>
        protected override void BeginUpdate(GameTime time)
        {
            if (SpatialProperties.MotionController != null)
                SpatialProperties.MotionController.Update(time);

            base.Update(time);
        }

        #endregion


        #region Utility

        /// <summary>
        /// Sets the centre-point of the camera.
        /// </summary>
        /// <param name="position">The centre-point of the camera.</param>
        public virtual void CentreCamera(Vector2 position)
        {
            SpatialProperties.Position = position - SpatialProperties.Size / 2;
        }


        /// <summary>
        /// Applies zoom to the camera relative to the centre-point.
        /// </summary>
        /// <param name="factor">The zoomfactor. Example: 1.5f --> Zoom in by 50%,  0.5f --> Zoom out by 50%</param>
        public virtual void Zoom(Single factor)
        {
            // Find the centre-point of the screen.
            Vector2 centre = SpatialProperties.Position + SpatialProperties.Size / 2;

            // Apply zoom effect.
            SpatialProperties.Size /= factor;

            // Maintain camera centre.
            CentreCamera(centre);
        }


        /// <summary>
        /// Transforms a point in world-space to pixel-space on the viewport.
        /// </summary>
        /// <param name="position">The point in world-space.</param>
        /// <returns>A point in pixel-space.</returns>
        public virtual Vector2 TransformPosition(Vector2 position)
        {
            return new Vector2((position.X - SpatialProperties.Position.X) * Viewport.RenderTarget.Width / SpatialProperties.Size.X,
                (position.Y - SpatialProperties.Position.Y) * Viewport.RenderTarget.Height / SpatialProperties.Size.Y);
        }


        /// <summary>
        /// Transforms a size in world-space to a size in pixels.
        /// </summary>
        /// <param name="size">The size in world-space.</param>
        /// <returns>The size in pixels.</returns>
        public virtual Vector2 TransformSize(Vector2 size)
        {
            return new Vector2(size.X * Viewport.RenderTarget.Width / SpatialProperties.Size.X,
                size.Y * Viewport.RenderTarget.Height / SpatialProperties.Size.Y);
        }

        #endregion

    }
}